﻿using AutoMapper;
using Core.SimpleTemp.Common;
using Core.SimpleTemp.Common.PagingQuery;
using Core.Toolkit.Core.Api;
using Core.Toolkit.Core.Entitys;
using Core.Toolkit.Core.Repositories.Internal;
using Core.Toolkit.PagingQuery;
using Microsoft.EntityFrameworkCore;
using MongoDB.Bson;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;

namespace Core.Toolkit.Core.Application
{
    public partial class BaseAppService<TDto, TEntity, TRepository, TDBcontext> where TEntity : Entity, new() where TRepository : BaseDBRepository<TDBcontext, TEntity> where TDto : Dto where TDBcontext : DbContext
    {
        protected TRepository _repository;

        public BaseAppService(TRepository repository) => _repository = repository;

        #region 删除操作

        public virtual Task DeleteAsync(TEntity entity, bool autoSave = true) => _repository.DeleteAsync(entity, autoSave);

        public virtual Task DeleteAsync(Guid id, bool autoSave = true) => _repository.DeleteAsync(id, autoSave);

        public virtual Task DeleteAsync(Expression<Func<TEntity, bool>> where, bool autoSave = true) => _repository.DeleteAsync(where, autoSave);

        public virtual Task DeleteBatchAsync(Guid[] ids, bool autoSave = true)
        {
            return this.DeleteAsync(it => ids.Contains(it.Id), autoSave);
        }
        #endregion

        public virtual async Task<TEntity> GetAsync(Guid id, IQueryable<TEntity> basqQuery = null)
        {
            return await _repository.GetAsync(id, basqQuery);
        }


        public async Task<List<TDto>> GetListAsync(Expression<Func<TEntity, bool>> where = null, Expression<Func<TEntity, object>> order = null, Expression<Func<TEntity, TEntity>> selector = null, IQueryable<TEntity> basqQuery = null, Action<IQueryable<TEntity>> beginQueryEventAction = null)
        {
            IQueryable<TEntity> iqueryable = basqQuery;
            if (iqueryable == null)
                iqueryable = _repository.QueryBase();
            if (where != null)
                iqueryable = iqueryable.Where(where);
            if (order != null)
                iqueryable = iqueryable.OrderBy(order);
            if (selector != null)
                iqueryable = iqueryable.Select(selector);
            //最后处理事件
            beginQueryEventAction?.Invoke(iqueryable);
            var dataList = await iqueryable.ToListAsync();
            return Mapper.Map<List<TDto>>(dataList);
        }

        public virtual async Task<IPageModel<TDto>> LoadPageOffsetAsync(LoadPageOffsetModel<TEntity, TDto> loadPageOffsetModel)
        {
            var iqueryable = BuildIqueyable(loadPageOffsetModel);
            var pageModelEntity = await _repository.LoadPageOffsetAsync(loadPageOffsetModel.Offset, loadPageOffsetModel.Limit, iqueryable);
            PageModel<TDto> pageModelDto = new PageModel<TDto>()
            {
                PageData = Mapper.Map<List<TDto>>(pageModelEntity.PageData),
                RowCount = pageModelEntity.RowCount
            };
            return pageModelDto;
        }

        public async Task<List<TDto>> ExportDataAsync(LoadPageOffsetModel<TEntity, TDto> loadPageOffsetModel)
        {
            var iqueryable = BuildIqueyable(loadPageOffsetModel);
            var entityList = await iqueryable.ToListAsync();
            return  Mapper.Map<List<TDto>>(entityList);
        }

        public async Task<List<TDto>> ExportDataAsync(LoadPageOffsetModelAuto<TEntity, TDto> loadPageOffsetModelAuto)
        {
            var iqueryable = BuildIqueyable(LoadPageOffsetConvert(loadPageOffsetModelAuto));
            var entityList = await iqueryable.ToListAsync();
            return Mapper.Map<List<TDto>>(entityList);
        }

        public virtual async Task<IPageModel<TDto>> LoadPageOffsetAsync(LoadPageOffsetModelAuto<TEntity, TDto> loadPageOffsetModelAuto)
        {
            var loadPageOffsetModel = LoadPageOffsetConvert(loadPageOffsetModelAuto);
            return await LoadPageOffsetAsync(loadPageOffsetModel);
        }


        private LoadPageOffsetModel<TEntity, TDto> LoadPageOffsetConvert(LoadPageOffsetModelAuto<TEntity, TDto> loadPageOffsetModelAuto)
        {
            LoadPageOffsetModel<TEntity, TDto> loadPageOffsetModel = new LoadPageOffsetModel<TEntity, TDto>
            {
                Limit = loadPageOffsetModelAuto.Limit,
                Offset = loadPageOffsetModelAuto.Offset,
                Model = loadPageOffsetModelAuto.Model,
                QueryWhere = loadPageOffsetModelAuto.QueryWhere?.BuildeQR(loadPageOffsetModelAuto.Model.ToEntity<TEntity>()),
                Order = loadPageOffsetModelAuto.Order,
                Selector = loadPageOffsetModelAuto.Selector,
                BasqQuery = loadPageOffsetModelAuto.BasqQuery,
                OrderList = loadPageOffsetModelAuto.OrderList,
                BeginSelectEventAction = loadPageOffsetModelAuto.BeginSelectEventAction
            };
            return loadPageOffsetModel;
        }

        private IQueryable<object> BuildIqueyable(LoadPageOffsetModel<TEntity, TDto> loadPageOffsetModel)
        {
            IQueryable<TEntity> iqueryable = loadPageOffsetModel.BasqQuery;
            //BaseQuery
            if (iqueryable == null)
                iqueryable = _repository.QueryBase();
            //Where
            if (!ReferenceEquals(loadPageOffsetModel.QueryWhere, null))
                iqueryable = iqueryable.Where(loadPageOffsetModel.QueryWhere);
            //Order
            if (loadPageOffsetModel.Order != null)
                iqueryable = iqueryable.OrderBy(loadPageOffsetModel.Order);
            //order list
            iqueryable = BuildOrder(iqueryable, loadPageOffsetModel);

            if (loadPageOffsetModel.BeginSelectEventAction != null)
                iqueryable = loadPageOffsetModel.BeginSelectEventAction?.Invoke(iqueryable);


            //Selector
            if (loadPageOffsetModel.Selector != null)
                return iqueryable.Select(loadPageOffsetModel.Selector);
            return iqueryable;
        }

        private IQueryable<TEntity> BuildOrder(IQueryable<TEntity> iqueryable, LoadPageOffsetModel<TEntity, TDto> loadPageOffsetModel)
        {
            if (loadPageOffsetModel.OrderList == null || !loadPageOffsetModel.OrderList.Any())
                return iqueryable;
            IOrderedQueryable<TEntity> orderedQueryable = null;
            bool first = true;
            foreach (var item in loadPageOffsetModel.OrderList)
            {
                if (first)
                {
                    orderedQueryable = OderyHelp.OrderBy<TEntity>(iqueryable, item.PropertyName, item.IsDesc, first);
                    first = false;
                }
                else
                    orderedQueryable = OderyHelp.OrderBy<TEntity>(orderedQueryable, item.PropertyName, item.IsDesc, first);
            }
            return orderedQueryable;
        }

        public virtual async Task<TDto> InsertAsync(TDto dto, bool autoSave = true)
        {
            var entity = await _repository.InsertAsync(Mapper.Map<TEntity>(dto), autoSave);
            return Mapper.Map<TDto>(entity);
        }


        public virtual async Task<TDto> UpdateAsync(TDto dto, bool autoSave = true, List<string> noUpdateProperties = null)
        {
            var entity = await _repository.UpdateAsync(Mapper.Map<TEntity>(dto), autoSave, noUpdateProperties);
            return Mapper.Map<TDto>(entity);
        }

        public virtual async Task<TEntity> UpdateAsync(TEntity entity, bool autoSave = true)
        {
            return await _repository.UpdateAsync(entity, autoSave);
        }

        #region 原生SQL
        public async Task<BsonArray> ExecuteQueryAsync(string sql, IEnumerable<DbParameter> paras = null)
        {
            return await this.ExecuteQueryAsync(sql, paras.ToArray());
        }
        public async Task<BsonArray> ExecuteQueryAsync(string sql, params DbParameter[] paras)
        {
            return await _repository.ExecuteQueryAsync(sql, paras);
        }
        public async Task<List<T>> ExecuteQueryAsync<T>(string sql, params DbParameter[] paras) where T : new()
        {
            return await _repository.ExecuteQueryAsync<T>(sql, paras);
        }

        public async Task<int> ExecuteNonQueryAsync(string sql, IEnumerable<DbParameter> paras = null)
        {
            return await this.ExecuteNonQueryAsync(sql, paras.ToArray());
        }

        public async Task<int> ExecuteNonQueryAsync(string sql, params DbParameter[] paras)
        {
            return await _repository.ExecuteNonQueryAsync(sql, paras);
        }

        public async Task<BsonArray> PageQueryAsync(string querySql, int offset, int limit, string order, IEnumerable<DbParameter> paras)
        {
            return await _repository.PageQueryAsync(querySql, offset, limit, order, paras.ToArray());

        }
        public async Task<List<T>> PageQueryAsync<T>(string querySql, int offset, int limit, string order, IEnumerable<DbParameter> paras) where T : new()
        {
            return await _repository.PageQueryAsync<T>(querySql, offset, limit, order, paras?.ToArray());
        }
        #endregion
    }
}
